/*
 * Copyright (C) 2010-2011 Josef Hardi <josef.hardi@gmail.com>
 * Copyright (C) 2010 Piotr Zagawa <piotr.zagawa@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package id.duapuluhdua.dakidaki.data.common;

import java.util.ArrayList;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;

import id.duapuluhdua.dakidaki.data.tables.DataTableGeoPoints;
import id.duapuluhdua.dakidaki.data.tables.DataTableTracks;
import id.duapuluhdua.dakidaki.data.tables.DataTableVersion;

/**
 *
 */
public class Database
{
  /** Constants */
  private static final String DB_NAME = "TrekJournal.db";

  /** Fields */
  private SQLiteDatabase mSqlDatabase;

  private ArrayList<DataTable> mVecTable;

  private DataTableVersion mTableVersion;
  private DataTableTracks mTableTracks;
  private DataTableGeoPoints mTableGeoPoints;

  private boolean bCreateTables = false;

  /**
   * Default constructor.
   *
   * @param context
   * @param createTables
   */
  public Database(Context context, boolean createTables)
  {
    this.bCreateTables = createTables;

    mSqlDatabase = null;
//    try {
//      dbSQL = context.openOrCreateDatabase(dbName, Context.MODE_PRIVATE, null);
//
//      initialize();
//
//      createTables();
//    }
//    catch (SQLiteException e) {
//      dbSQL = null;
//      UserAlert.show(context, UserAlert.Result.DB_ACCESS_ERROR);
//    }
  }

  /* Initialize the required tables */
  private void initialize()
  {
    mTableVersion = new DataTableVersion(this);
    mTableTracks = new DataTableTracks(this);
    mTableGeoPoints = new DataTableGeoPoints(this);

    mVecTable = new ArrayList<DataTable>();
    mVecTable.add(mTableTracks);
    mVecTable.add(mTableGeoPoints);
  }

  /* Creates the tables */
  private void createTables()
  {
    if (bCreateTables) {
      // Version table first
      mTableVersion.createTable();
      for (DataTable table : mVecTable) {
        table.createTable();
      }
    }
    else {
      // Version table first
      mTableVersion.checkExistence();
      for (DataTable table : mVecTable) {
        table.checkExistence();
      }
    }
  }

  /**
   * Closes the database.
   */
  public void close()
  {
    if (mSqlDatabase != null) {
      mSqlDatabase.close();
    }
  }

  /**
   * Returns the status of the database.
   *
   * @return true if the database is currently open.
   */
  public boolean isOpen()
  {
    boolean bValue = false;
    synchronized(mSqlDatabase)
    {
      bValue = mSqlDatabase.isOpen();
    }
    return bValue;
  }

  /**
   * Gets the object of Version table.
   *
   * @return the {@link DataTableVersion} object.
   */
  public DataTableVersion getTableVersion()
  {
    return mTableVersion;
  }

  /**
   * Gets the object of Tracks table.
   *
   * @return the {@link DataTableTracks} object.
   */
  public DataTableTracks getTableTracks()
  {
    return mTableTracks;
  }

  /**
   * Gets the object of Geo-points table.
   *
   * @return the {@link DataTableGeoPoints} object.
   */
  public DataTableGeoPoints getTableGeoPoints()
  {
    return mTableGeoPoints;
  }

  /**
   * Checks whether the table exists in the database or not.
   *
   * @param tableName the table name.
   * @return true if the table already is defined in the database, or false
   * otherwise.
   */
  public boolean tableExists(String tableName)
  {
    boolean bResult = false;

    String sql = String.format(
        "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '%s'",
        tableName);

    Cursor cr = null;
    synchronized(mSqlDatabase)
    {
      cr = mSqlDatabase.rawQuery(sql, null);
    }

    if (cr != null) {
      if (cr.getCount() > 0) {
        bResult = true;
      }
      cr.close();
    }
    return bResult;
  }

  /**
   * Checks whether the database is having a transaction.
   *
   * @return Returns true if there is a pending transaction, or false otherwise.
   */
  public boolean isInTransaction()
  {
    boolean bValue = false;
    synchronized (mSqlDatabase)
    {
      bValue = mSqlDatabase.inTransaction();
    }
    return bValue;
  }

  /**
   * Begins the transaction.
   */
  public void transactionBegin()
  {
    synchronized (mSqlDatabase)
    {
      mSqlDatabase.beginTransaction();
    }
  }

  /**
   * Transaction committed.
   */
  public void transactionCommit()
  {
    synchronized (mSqlDatabase)
    {
      mSqlDatabase.setTransactionSuccessful();
    }
  }

  /**
   * Ends the transaction.
   */
  public void transactionEnd()
  {
    synchronized (mSqlDatabase)
    {
      mSqlDatabase.endTransaction();
    }
  }

  /**
   * Executes an SQL data definition statements (e.g., CREATE TABLE,
   * DROP TABLE, ALTER, etc.)
   *
   * @param sql an SQL string.
   * @return true if the execution generates no exception, or false otherwise.
   */
  public boolean executeSql(String sql)
  {
    try {
      synchronized(mSqlDatabase)
      {
        mSqlDatabase.execSQL(sql);
      }
      return true;
    }
    catch (SQLiteException e) {
      return false;
    }
  }

  /**
   * Executes an SQL query select statement.
   *
   * @param sql an SQL string.
   * @return A {@link Cursor} object.
   */
  public Cursor query(String sql)
  {
    Cursor cr = null;
    synchronized(mSqlDatabase)
    {
      cr = mSqlDatabase.rawQuery(sql, null);
    }
    return cr;
  }

  /**
   * Executes an SQL insert statement.
   *
   * @param tableName
   *                The table name.
   * @param values
   *                A collection of values that are going to be inserted.
   * @return A row id.
   */
  public long insert(String tableName, ContentValues values)
  {
    long newRowId = -1;
    synchronized(mSqlDatabase)
    {
      newRowId = mSqlDatabase.insert(tableName, null, values);
    }
    return newRowId;
  }

  /**
   * Executes an SQL update statement.
   *
   * @param tableName
   *                The table name.
   * @param values
   *                A collection of new values.
   * @param where
   *                The optional WHERE condition.
   * @return The number of updated rows.
   */
  public int update(String tableName, ContentValues values, String where)
  {
    int rowsUpdated = 0;
    synchronized(mSqlDatabase)
    {
      rowsUpdated = mSqlDatabase.update(tableName, values, where, null);
    }
    return rowsUpdated;
  }

  /**
   * Executes an SQL delete statement.
   *
   * @param tableName
   *                The table name.
   * @param where
   *                The optional WHERE condition.
   * @return A row id.
   */
  public int delete(String tableName, String where)
  {
    int rowsDeleted = 0;
    synchronized (mSqlDatabase)
    {
      rowsDeleted = mSqlDatabase.delete(tableName, where, null);
    }
    return rowsDeleted;
  }
}
